#ifndef __ZSINGLELINE_HPP
#define __ZSINGLELINE_HPP

#include "ZUtf8Char.hpp"
#include "../../datastructureZ/LinkedListZ.h"
#include "../../common.hpp"

using namespace std;
using namespace datastructureZ;

class ZSingleLineEdit
{
	public:typedef LinkedList<ZUtf8Char> LineType;
	
	private:size_t size;		//编辑框在屏幕中的大小 x
	private:size_t location[2];	//编辑框在屏幕上的位置 x, y
	
	private:size_t relativeCursorPosition; //跟踪光标的坐标（视觉列x）
	
	private:LineType* content;		//内容
	private:LineType::iterator current; //光标迭代器
	
	private:size_t pageStartCol;  //起始显示列
	
	private:char* spaceLine;
	private:char* boxLine;
	
	private:bool visibility;
	
	public:ZSingleLineEdit(size_t locX, size_t locY, size_t sizeX):
	size(sizeX), location{locX, locY}, relativeCursorPosition(0), 
	pageStartCol(0), spaceLine(nullptr), 
	boxLine(nullptr), visibility(false)
	{
		if(sizeX < 10) throw "Too small";
		
		content = new LineType;
		current = content->begin();
		
		spaceLine = new char[size + 1];
		memset(spaceLine, ' ', size);
		spaceLine[size] = '\0';
		
		boxLine = new char[size + 1];
		memset(boxLine, '_', size);
		boxLine[size] = '\0';
	}
	
	public:bool visible(void)
	{
		return visibility;
	}
	
	private:void disable(void)
	{
		delete content;
	}
	
	public:~ZSingleLineEdit(void)
	{
		if(spaceLine != nullptr) delete spaceLine;
		if(boxLine != nullptr)	 delete boxLine;
		disable();
	}
	
	//清除内容
	public:void clear(void)
	{
		disable();
		relativeCursorPosition = 0;
		pageStartCol = 0;
		
		content = new LineType;
		current = content->begin();
	}
	
	//清屏（用于刷新）
	private:void clean(void)
	{
		GOTOXY(location[0], location[1] + 1);
		cout << spaceLine << flush;
	}
	
	public:void resize(size_t x)
	{
		
	}
	
		
	public:void printNew(void)
	{
		while(_printNew());
	}
	
	private:bool _printNew(void)
	{
		//清屏
		clean();
	
		//来到内容第一行
		GOTOXY(location[0], location[1] + 1);
		
		//跟踪当前输出位置（控件视觉绝对坐标x, y，GOTOXY需要提供绝对坐标）
		size_t currentPos = location[0];
		size_t width = 1, compressed = 0;
		
		//遍历迭代器
		LineType::iterator eachChar;
		bool flag = false;
		
			//输出左边框
			cout << ' ';
			++currentPos;
			//从第1列移到页起始显示列
			eachChar = content->begin();
			size_t startCol = 0;
			for(; startCol < pageStartCol; ++eachChar)
			{//对于每个字符
				//横向调整视区
				if(eachChar == current)
				{
					if(startCol > 3) pageStartCol = startCol - 3;
					else pageStartCol = 0;
					return true;
				}
				//如果还没到达页起始显示列，就已经到达行尾，退出当前行
				if(eachChar.isEnd()) goto nextln;
				//记载跳过的宽幅
				startCol += eachChar->getWidth(startCol);
			}
			
			//多宽幅字符对齐
			if(startCol > pageStartCol)
			{
				size_t diff = startCol - pageStartCol;
				for(size_t i = 0; i < diff; ++i) //对于tab可能会有8格宽度
				{//如果跳过的字符宽幅超过了起始列，进行对齐
					cout << ' ';
				}
				currentPos += diff;
				compressed += diff;
			}
			
			//对于一行中的每个字符
			while(!eachChar.isEnd())
			{
				//计算字符的显示宽度
				//制表符宽度单独计算：8 - (n%8)
				if(*eachChar == '\t')
				{
					width = 8 - (startCol % 8);
					//每次刷新都更新能看到的制表符的宽度参数（宽度会变化）
					eachChar->updateTabCharWidth(width);
					compressed += width;
				}
				else width = eachChar->getWidth();
				
				//如果显示宽度大于等于编辑框宽度，退出当前行（+1为跳过边缘）
				if(width + currentPos + 1 >= location[0] + size)
				{
					//横向调整视区
					if(!flag)
					{
						pageStartCol = relativeCursorPosition + 3 - 
							size + current->getWidth(relativeCursorPosition) + 
							compressed;
						return true;
					}
					else break;
				}
				
				//输出字符（字符可能被光标所高亮选中）
				if(eachChar == current)
				{
					cout << bgWhite << *(eachChar++) << colorEnd;
					flag = true;
				}
				else cout << *(eachChar++);
				
				//当前输出位置后移字符宽度
				currentPos += width;
				startCol += width;
				
				//调整输出光标位置
				GOTOXY(currentPos, location[1] + 1);
			}
			
			//若光标置于行末，单独高亮显示
			if(eachChar == current  &&  eachChar.isEnd())
			{
				cout << bgWhite << ' ' << colorEnd;
				++currentPos;
				flag = true;
			}
		nextln:
		
		//输出下边框等号线
		GOTOXY(location[0], location[1] + 2);
		cout << White << boxLine << colorEnd << flush;
		return false;
	}
	
	public:void pgRight(size_t n = 1)
	{
		pageStartCol += n;
	}
	
	public:void pgLeft(size_t n = 1)
	{
		if(pageStartCol == 0 || n > pageStartCol) pageStartCol = 0;
		else pageStartCol -= n;
	}
	
	public:void cursorRight(size_t n = 1)
	{
		//链表顺序访问    如果不是在行尾右移光标
		while(n--) if(!current.isEnd())
		{
			//光标列跟踪：加上当前光标所选中字符的宽度
			relativeCursorPosition += current->getWidth();
			//如果光标到达右边框，视区向右移动
			//if(relativeCursorPosition + 1 + current->getWidth() >= pageStartCol + size) pgRight(current->getWidth());
			
			//选中后一个字符
			++current;
		}
	}
	
	public:void cursorLeft(size_t n = 1)
	{
		while(n--) if(current != content->begin())
		{
			--current;
			relativeCursorPosition -= current->getWidth();
			//if(relativeCursorPosition + 1 <= pageStartCol) pgLeft(current->getWidth());
		}
	}
	
	private:int insert(ZUtf8Char& c)
	{
		if(c == (char)27) return 0;		//ESC键
		else if(c == '\n'  ||  c == '\r') return 2;
		else if(c == (char)127  ||  c == (char)8) //退格
		{
			LineType::iterator temp = current;
			//退格删除的是光标选中字符的前一个字符
			if(!(--temp).isEnd())
			{//如果不是删除行首，普通地在本行中删除一个字符
				//光标前移前一个字符的宽度（tab不确定的宽度由每次printNew计算维护）
				relativeCursorPosition -= temp->getWidth();
				//删除字符
				temp.erase();
				//erase方法删除后会后移，将temp迭代器移回被删除字符的位置
				--temp;
				//调整视区
				/*if(relativeCursorPosition + 1 <= pageStartCol)
				{
					if(temp.isEnd()) pageStartCol = 0;
					else pgLeft(temp->getWidth());
				}*/
			}
		}
		else//普通输入
		{
			//计算输入字符的宽度
			if(c == '\t') c.updateTabCharWidth(8 - (relativeCursorPosition % 8));
			size_t wid = c.getWidth();
			//将字符插入到光标前部
			current.insert_to_front(c);
			//跟踪光标
			relativeCursorPosition += wid;
			//调整视区
			//if(relativeCursorPosition + 2 >= pageStartCol + size) pgRight(wid);
		}
		
		return 0;
	}
	
	//生成消息编辑结果字符串
	private:string dmp(void)
	{
		string result("");
		
		for
		(
			LineType::iterator eachC = content->begin();
			!eachC.isEnd();
			++eachC
		)
		{
			result.append((const char*)(*eachC), eachC->getMemorySize());
		}
		
		return result;
	}
	
	private:bool insertMode(bool refresh = true)
	{
		int r = 0;
		for(ZUtf8Char c;;)
		{
			if(refresh) printNew();
			c.get(getch);
			if(c == (char)27)
			{
				if(getch() == 91) switch(getch())
				{
					case 67: cursorRight();	break;
					case 68: cursorLeft();	break;
				}
			}
			else if((r = insert(c)) == 1) return false;
			else if(r == 2) return true;
		}
	}
	
	public:string run(void)
	{
		bool flg = false;
		visibility = true;
		printNew();
		
		flg = insertMode();
			
		printNew();
		
		
		visibility = false;
		if(flg) return dmp();
		else return "";
	}
};

#endif